/**
 * Project: yui3-common-mybatisx-base
 * Class RelectionUtils
 * Version 1.0
 * File Created at 2020-12-23
 * $Id$
 * author yuyi
 * email 1060771195@qq.com
 */
package yui.comn.mybatisx.core.toolkit;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import lombok.extern.slf4j.Slf4j;
import yui.comn.mybatisx.annotation.VoDef;
import yui.comn.mybatisx.core.conditions.Wrapper;
import yui.comn.mybatisx.core.conditions.update.UpdateWrapper;
import yui.comn.mybatisx.core.exceptions.MybatisLinkException;

/**
 * <p>
 * 反射工具类
 * </p>
 *
 * @author yuyi (1060771195@qq.com)
 */
@Slf4j
public class EntityUtils {
    
	public static Serializable getPrimaryKeyValueByVo(Object entity) {
		if (null == entity) {
			return null;
		}
		
		String keyColumn = TableInfoHelper.getTableInfo(entity.getClass()).getKeyColumn();
		return (Serializable) ReflectionKit.getFieldValue(entity, keyColumn);
	}
	
    public static <T> Collection<Serializable> listPrimaryKeyValueByVos(Collection<T> voList) {
        if (CollectionUtils.isEmpty(voList)) {
            return Collections.emptyList();
        }
        
        Collection<Serializable> list = new ArrayList<>();
        String keyColumn = null;
        for (T vo : voList) {
        	if (null == keyColumn) {
        		keyColumn = TableInfoHelper.getTableInfo(vo.getClass()).getKeyColumn();
        	}
        	list.add((Serializable) ReflectionKit.getFieldValue(vo, keyColumn));
        }
        return list;
    }
    
    public static Serializable getPrimaryKeyValueByDto(Object dto) {
        if (null == dto) {
            return null;
        }
        
        Object vo = ReflectionKit.getFieldValue(dto, getFieldName(dto));
        return getPrimaryKeyValueByVo(vo);
    }
    
    private static String getFieldName(Object dto) {
        List<Field> fieldList = ReflectionKit.getFieldList(dto.getClass());
        
        String fieldName = null;
        for (Field field : fieldList) {
            VoDef anno = field.getAnnotation(VoDef.class);
            if (null != anno) {
                fieldName = field.getName();
                break;
            }
        }
        
        // 如果没有注解，使用dto命名规则，比如 SysUserDto --> sysUserVo
        if (StringUtils.isBlank(fieldName)) {
            String simpleName = dto.getClass().getSimpleName();
            fieldName = StringUtils.firstToLowerCase(simpleName.substring(0, simpleName.length() - 3) + "Vo");
        }
        
        return fieldName;
    }
    
    public static <D> Collection<Serializable> listPrimaryKeyValueByDtos(Collection<D> dtoList) {
        if (CollectionUtils.isEmpty(dtoList)) {
            return Collections.emptyList();
        }
        
        Collection<Serializable> list = new ArrayList<>();
        
        String fieldName = null;
        String keyColumn = null;
        for (D d : dtoList) {
            if (null == fieldName) {
                fieldName = getFieldName(d);
            }
            Object voVal = ReflectionKit.getFieldValue(d, fieldName);
            if (null == keyColumn) {
            	keyColumn = TableInfoHelper.getTableInfo(voVal.getClass()).getKeyColumn();
            }
            list.add((Serializable) ReflectionKit.getFieldValue(voVal, keyColumn));
        }
        
        return list;
    }
    
    public static Object getVo(Object dto) {
        if (null == dto) {
            return null;
        }
        String fieldName = getFieldName(dto);
        return ReflectionKit.getFieldValue(dto, fieldName);
    }
    
    public static <D> Collection<?> listVo(Collection<D> dtoList) {
        if (CollectionUtils.isEmpty(dtoList)) {
            return Collections.emptyList();
        }
        
        Collection<Object> list = new ArrayList<>();
        String fieldName = null;
        for (D d : dtoList) {
            if (null == fieldName) {
                fieldName = getFieldName(d);
            }
            Object voVal = ReflectionKit.getFieldValue(d, fieldName);
            list.add(voVal);
        }
        
        return list;
    }
    
    @SuppressWarnings("unchecked")
    public static <E extends IPage<D>, T, D> E pageVo(E page) {
        IPage<T> p = new Page<>();
        
        p.setRecords((List<T>) listVo(page.getRecords()));
        p.setCurrent(page.getCurrent());
        p.setPages(page.getPages());
        p.setSize(page.getSize());
        p.setTotal(page.getTotal());
        
        return (E) p;
    }
    
    public static Map<String, Object> getMap(String colomns, Object... values) {
        Map<String, Object> columnMap = new HashMap<>();
        String[] cols = colomns.split(StringPool.COMMA);
        for (int i = 0; i < cols.length; i++) {
            columnMap.put(cols[i], values[i]);
        }
        return columnMap;
    }
    
    public static <D, V> IPage<D> getPage(Wrapper<V> wrapper) {
        if (null == wrapper) {
            return new Page<D>();
        }
        return new Page<D>(wrapper.getPageNo(), wrapper.getPageSize(), wrapper.isSearchCount());
    }

    /**
     * 更新实例转更新对象
     * @param entityList
     * @param args
     * @return
     * @param <V>
     */
    public static <V> Collection<Wrapper<V>> getUpdateWrapperList(Collection<V> entityList, String... args) {
        if (CollectionUtils.isEmpty(entityList)) {
            return Collections.emptyList();
        }

        List<Wrapper<V>> wrapperList = new ArrayList<>();
        entityList.forEach(item -> {
            wrapperList.add(getUpdateWrapper(item, args));
        });
        return wrapperList;
    }

    public static <V> Wrapper<V> getUpdateWrapper(V entity, String... args) {
        if (null == entity) {
            return null;
        }

        Class<?> clazz = entity.getClass();
        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
        String keyColumn = tableInfo.getKeyColumn();

        Object idValObj = getVal(clazz, entity, keyColumn);
        if (null == idValObj) {
            throw new MybatisLinkException("实体类主键为空，无法更新");
        }

        UpdateWrapper<V> wrapper = new UpdateWrapper<>();
        wrapper.eq(keyColumn, idValObj);

        for (String arg : args) {
            wrapper.set(arg, getVal(clazz, entity, arg));
        }

        return wrapper;
    }

    private static <V> Object getVal(Class<?> clazz, V obj, String arg) {
        String methodStr = StringUtils.underlineToCamel("get_" + arg);
        try {
            Method method = clazz.getMethod(methodStr);
            return method.invoke(obj);
        } catch (Exception e) {
            log.error("entity 转 updateWrapper 反射对象失败", e);
        }
        return null;
    }

}
